package com.zp.security.oauth2.config;

import com.zp.common.base.constant.Atom;
import com.zp.common.base.entity.SecurityUser;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.DefaultUserAuthenticationConverter;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

import java.util.LinkedHashMap;

/**
 * 访问令牌配置
 *
 * @author zp
 */
@Configuration
@Data
@ConfigurationProperties("jwt")
public class AccessTokenConfig {
    /**
     * JWT的秘钥
     */
    private String secret;

    /**
     * userDetailService
     */
    @Autowired
    @Qualifier("loginServiceImpl")
    private UserDetailsService userDetailsService;

    /**
     * 令牌的存储策略
     */
    @Bean
    public TokenStore tokenStore() {
        //使用JwtTokenStore生成JWT令牌
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    /**
     * JwtAccessTokenConverter
     * TokenEnhancer的子类，在JWT编码的令牌值和OAuth身份验证信息之间进行转换。
     */
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter(){
        //适用自定义的JwtAccessTokenConverter
        JwtAccessTokenConverter converter = new MyJwtAccessTokenConverter();
        // 设置秘钥
        converter.setSigningKey(secret);

        /**
         * 此处还要处理刷新令牌时刷新的令牌也要带着附加信息
         * 刷新令牌时会调用DefaultUserAuthenticationConverter的extractAuthentication方法
         * 将解析到的认证信息转化为用户信息,默认只有用户名，因此我们需要注入userDetailsService来获得一个SecurityUser
         * 从而继续走enhance方法
         * */
        //创建默认的DefaultUserAuthenticationConverter
        DefaultUserAuthenticationConverter userAuthenticationConverter = new DefaultUserAuthenticationConverter();
        //注入UserDetailService
        userAuthenticationConverter.setUserDetailsService(userDetailsService);
        DefaultAccessTokenConverter tokenConverter = new DefaultAccessTokenConverter();
        tokenConverter.setUserTokenConverter(userAuthenticationConverter);
        converter.setAccessTokenConverter(tokenConverter);
        return converter;


    }


    /**
     * JWT令牌增强，继承JwtAccessTokenConverter
     * 将业务所需的额外信息放入令牌中，这样下游微服务就能解析令牌获取
     */
    public static class MyJwtAccessTokenConverter extends JwtAccessTokenConverter{
        /**
         * 重写enhance方法，在其中扩展
         */
        @Override
        public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
            Object principal=authentication.getUserAuthentication().getPrincipal();

            //如果认证的信息是SecurityUser的话
            if (principal instanceof SecurityUser){
                //获取userDetailService中查询到用户信息
                SecurityUser user=(SecurityUser)authentication.getUserAuthentication().getPrincipal();
                //将额外的信息放入到LinkedHashMap中
                LinkedHashMap<String,Object> extendInformation=new LinkedHashMap<>();
                //设置用户的手机号和邮箱
                extendInformation.put(Atom.EMAIL,user.getEmail());
                extendInformation.put(Atom.TELEPHONE,user.getTelephone());
                //添加到additionalInformation
                ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(extendInformation);
            }
            return super.enhance(accessToken, authentication);
        }
    }
}
